#include "timer_func.h"

#include <sys/time.h>
#include <time.h>

#include "../register_init_cfg/register_init.h"

igmp_pkt *g_igmp_query_pkt = NULL;
igmp_pkt *g_igmp_special_query_pkt = NULL;
u64 g_timer_array[MAX_TIMER_NUM];
extern multicast_context g_multicast_context_;

u8 igmp_query_count = 0;

// 获取当前时刻（ns）
u64 get_cur_nano_sec()
{
  unsigned long CurNanoSec;
  struct timespec tv;
  clock_gettime(ITIMER_REAL, &tv);
  CurNanoSec = tv.tv_sec * 1000000000 + tv.tv_nsec;

  return CurNanoSec;
}

/**
 * 计算两个时间的差值
 * @param cur_time 当前时刻，64bit 以纳秒为单位
 * @param start_time 起始时刻，64bit 以纳秒为单位
 * @return 两个时刻之间的差值
 */
u64 get_diff(u64 cur_time, u64 start_time)
{
  u64 diff = cur_time - start_time;
  return diff;
}

// 构建通用组查询报文
int igmp_pkt_create()
{
  u8 temp_mac[6];
  unsigned short *heard = NULL;

  // 初始化通用组和特定组查询报文的内存空间
  g_igmp_query_pkt = (igmp_pkt *)malloc(MAX_PKT_LEN);
  if (NULL == g_igmp_query_pkt)
  {
    printf("malloc buf fail\n");
    return -1;
  }

  g_igmp_special_query_pkt = (igmp_pkt *)malloc(MAX_PKT_LEN);
  if (NULL == g_igmp_query_pkt)
  {
    printf("malloc buf fail\n");
    return -1;
  }

  bzero(g_igmp_query_pkt, MAX_PKT_LEN);
  bzero(g_igmp_special_query_pkt, MAX_PKT_LEN);

  // igmp头赋值
  g_igmp_query_pkt->igmp_header.type = 0x11;
  g_igmp_query_pkt->igmp_header.max_response_time = g_multicast_context_.querier_info.querier_maxresptime;
  g_igmp_query_pkt->igmp_header.multicast_addr[0] = 0;
  g_igmp_query_pkt->igmp_header.multicast_addr[1] = 0;
  g_igmp_query_pkt->igmp_header.multicast_addr[2] = 0;
  g_igmp_query_pkt->igmp_header.multicast_addr[3] = 0;

  g_igmp_query_pkt->igmp_header.check_sum = 0;
  heard = (unsigned short *)((u8 *)g_igmp_query_pkt + 14 + 20); // eth+ip=14+20
  g_igmp_query_pkt->igmp_header.check_sum = ip_csum(heard, 8);  // 检验和

  // ip头赋值
  g_igmp_query_pkt->ip_header.ver = 0x45;
  g_igmp_query_pkt->ip_header.ip_len = htons(28);
  g_igmp_query_pkt->ip_header.ttl = 1;
  g_igmp_query_pkt->ip_header.protocol = 0x02;
  // 224.0.0.1
  g_igmp_query_pkt->ip_header.dst_ip[0] = 224;
  g_igmp_query_pkt->ip_header.dst_ip[1] = 0;
  g_igmp_query_pkt->ip_header.dst_ip[2] = 0;
  g_igmp_query_pkt->ip_header.dst_ip[3] = 1;

  memcpy(g_igmp_query_pkt->ip_header.src_ip,
         g_multicast_context_.querier_info.querier_sip, 4);

  g_igmp_query_pkt->ip_header.check_sum = 0;
  heard = (unsigned short *)((u8 *)g_igmp_query_pkt + 14);    // eth=14
  g_igmp_query_pkt->ip_header.check_sum = ip_csum(heard, 20); // 检验和

  // eth头赋值
  g_igmp_query_pkt->eth_header.eth_type = htons(0x0800);
  get_hcp_mac_from_mid(temp_mac, g_multicast_context_.querier_info.querier_mid);
  memcpy(g_igmp_query_pkt->eth_header.smac, temp_mac, 6);
  // ip 224.0.0.1->mac 01:00:5e:00:00:01
  g_igmp_query_pkt->eth_header.dmac[0] = 0x01;
  g_igmp_query_pkt->eth_header.dmac[1] = 0x00;
  g_igmp_query_pkt->eth_header.dmac[2] = 0x5e;
  g_igmp_query_pkt->eth_header.dmac[3] = 0x00;
  g_igmp_query_pkt->eth_header.dmac[4] = 0x00;
  g_igmp_query_pkt->eth_header.dmac[5] = 0x01;

  return 0;
}

// 构建特定组查询报文
int igmp_special_pkt_create(u8 *ip)
{
  unsigned short *heard;
  u8 local_mac[6];

  // 更新igmp头
  memcpy(g_igmp_special_query_pkt->igmp_header.multicast_addr, ip, 4);

  g_igmp_special_query_pkt->igmp_header.check_sum = 0;
  heard = (unsigned short *)(g_igmp_special_query_pkt + 14 + 20);      // eth+ip=14+20
  g_igmp_special_query_pkt->igmp_header.check_sum = ip_csum(heard, 8); // 检验和

  // 更新ip头
  memcpy(g_igmp_special_query_pkt->ip_header.dst_ip, ip, 4);

  g_igmp_special_query_pkt->ip_header.check_sum = 0;
  heard = (unsigned short *)(g_igmp_special_query_pkt + 14);          // eth=14
  g_igmp_special_query_pkt->ip_header.check_sum = ip_csum(heard, 20); // 检验和

  // 更新eth头（ip后23bit映射mac后23bit，24bit为固定0）

  g_igmp_special_query_pkt->eth_header.dmac[3] = ip[1] & 0x7F;
  g_igmp_special_query_pkt->eth_header.dmac[4] = ip[2];
  g_igmp_special_query_pkt->eth_header.dmac[5] = ip[3];

  get_hcp_mac_from_mid(local_mac, g_multicast_context_.querier_info.querier_mid);
  tsmp_tunnal_encapsulation(local_mac, 60, (u8 *)g_igmp_special_query_pkt); // 发送特定组查询报文

  return 0;
}

// 查询器超时
int querier_timeout(u64 cur_time_count_ns)
{
  // 查询器使能开启
  if (g_multicast_context_.querier_info.querier_enable == 1)
  {
    // 第一次查询
    if (!igmp_query_count)
    {
      igmp_pkt_create();
      memcpy(g_igmp_special_query_pkt, g_igmp_query_pkt, MAX_PKT_LEN);
      igmp_query_count++;
    }
    u8 local_mac[6];
    get_hcp_mac_from_mid(local_mac,
                         g_multicast_context_.querier_info.querier_mid);
    tsmp_tunnal_encapsulation(local_mac, 60, (u8 *)g_igmp_query_pkt); // 发送通用组查询报文
  }
  return 0;
}

// 转发表老化超时
int table_aging_timeout(u64 cur_time_count_ns)
{
  int i = 0;
  int j = 0;
  int k = 0;
  node_multicast *temp_node_info = NULL;
  multicast_table_entry *temp_table_info = NULL;

  // 遍历所有节点
  for (i = 0; i < MAX_NODE_NUM; i++)
  {
    int update = 0;
    temp_node_info = g_multicast_context_.node_multicast_context[i];

    if (temp_node_info == NULL) // 节点不存在
      continue;

    // 遍历动态组播表项
    for (j = temp_node_info->multicast_table_info.static_multicast_entry_size;
         j < MAX_MULTICAST_ENTRY_NUM; j++)
    {
      temp_table_info = temp_node_info->multicast_table_info.table[j];

      if (temp_table_info == NULL) // 表项不存在
        continue;

      if (temp_table_info->valid == 0) // 无效表项
        continue;

      // 遍历所有端口
      for (k = 0; k < MAX_PORT_NUM; k++)
      {
        // 端口未启用
        if (!((temp_table_info->port >> k) & 1))
          continue;

        u64 time_out = get_diff(cur_time_count_ns,
                                temp_table_info->aging_time_info.port_aging_time_info[k]->time_cnt);

        // 成员端口超时
        if (time_out > (u64)temp_node_info->cfg_para_info.member_port_aging_time * 1000000000)
        {
          temp_table_info->port &= ~(1 << k); // 删除该端口

          temp_table_info->aging_time_info.port_aging_time_info[k]->time_cnt = 0; // 重置计时器

          update = 1;
        }
      }

      // 该组播表无成员
      if (temp_table_info->port == 0)
        temp_table_info->valid = 0; // 删除该表项

      // 存在表项更新
      if (update)
        update_table(i, j); // 更新转发表
    }
  }
}

// 动态路由老化超时
int route_port_aging_timeout(u64 cur_time_count_ns)
{
  int i = 0;
  int j = 0;
  node_multicast *temp_node_info = NULL;

  // 遍历所有节点
  for (i = 0; i < MAX_NODE_NUM; i++)
  {
    int update = 0;
    temp_node_info = g_multicast_context_.node_multicast_context[i];

    // 节点不存在
    if (temp_node_info == NULL)
      continue;

    // 没有动态路由端口
    if (temp_node_info->route_port_info.dynamic_route_port == 0)
      continue;

    // 遍历所有端口
    for (j = 0; j < MAX_PORT_NUM; j++)
    {
      // 端口未启用
      if (!((temp_node_info->route_port_info.dynamic_route_port >> j) & 1))
        continue;

      u64 time_out = get_diff(cur_time_count_ns,
                              temp_node_info->route_port_info.aging_time_info.port_aging_time_info[j]->time_cnt);

      // 路由端口超时
      if (time_out > (u64)temp_node_info->cfg_para_info.route_port_aging_time * 1000000000)
      {
        temp_node_info->route_port_info.dynamic_route_port &= ~(1 << j); // 删除该端口

        temp_node_info->route_port_info.aging_time_info.port_aging_time_info[j]->time_cnt = 0; // 重置计时器

        update = 1;
      }
    }

    // 存在路由更新
    if (update)
      update_port(i); // 更新动态路由
  }
}

// 超时处理函数
int timer_func(u64 cur_time_count_ns)
{
  int ret = 0;
  int i = 0;

  for (i = 0; i < MAX_TIMER_NUM; i++)
  {
    // 每个数组对应不同的处理函数，因此使用case语句
    switch (i)
    {

    case QUERIER_TIMEOUT_IDX:
      if (get_diff(cur_time_count_ns, g_timer_array[i]) >=
          (u64)g_multicast_context_.querier_info.querier_period * 1000000000)
      {
        ret = querier_timeout(cur_time_count_ns); // 查询器超时
        g_timer_array[i] = cur_time_count_ns;     // 更新超时计时器
      }
      break;

    case TABLE_AGING_TIMEOUT_IDX:
      if (get_diff(cur_time_count_ns, g_timer_array[i]) >=
          TABLE_QUERY_TIME * 1000000000)
      {
        ret = table_aging_timeout(cur_time_count_ns); // 转发表老化超时
        g_timer_array[i] = cur_time_count_ns;         // 更新超时计时器
      }
      break;

    case ROUTE_PORT_AGING_TIMEOUT_IDX:
      if (get_diff(cur_time_count_ns, g_timer_array[i]) >=
          PORT_QUERY_TIME * 1000000000)
      {
        ret = route_port_aging_timeout(cur_time_count_ns); // 动态路由老化超时
        g_timer_array[i] = cur_time_count_ns;              // 更新超时计时器
      }
      break;

    default:
      break;
    }
  }

  return ret;
}

void igmp_free()
{
  free(g_igmp_query_pkt);
  free(g_igmp_special_query_pkt);
}